/**************************
***
*** dept: test
*** author: htj
***
*************************/
#include <streambuf>
#include <string>


namespace std {


template<class Reader, class charT, typename traits = char_traits<charT> >
class istreambufadaptor: public basic_streambuf<charT,traits>
{
//friend  streamadaptor<class Stream, Reader> MakeStreamAdaptor<>(Stream& s,Reader& r);
//friend  class streamadaptor;
	Reader& _r;
 	charT *_in;
 	streamsize _in_len;
public: //TODO these two func should not be public 
 	istreambufadaptor(const istreambufadaptor&);
 	istreambufadaptor& operator=(const istreambufadaptor&);
public:
    typedef charT   char_type;
    typedef typename traits::int_type	    int_type;
    typedef typename traits::pos_type	    pos_type;
    typedef typename traits::off_type	    off_type;
    typedef traits			    traits_type;
 
	istreambufadaptor(Reader& r,ios_base::openmode mode = ios_base::in | ios_base::binary): _r(r),_in(0),_in_len(0){
  		setg(_in,_in,_in);
	}

 	~istreambufadaptor() {
  		if(_in) free_in_buf();
 	}

private:
 	
	void init_in_buf() {
  		_in_len = 512;
  		_in = new charT[_in_len];
  		setg(_in,_in,_in);
 	}
 
	void free_in_buf() {
  		delete [] _in;
  		_in = 0;
  		_in_len = 0;
  		setg(_in,_in,_in);
 	}
protected:
 
	virtual int_type underflow() {
  		if(this->egptr() == 0) init_in_buf();
		if(this->egptr() == _in + _in_len) setg(_in,_in,_in);
  		
		int tmp = _r.read(this->egptr(),_in + _in_len - this->egptr());
  		if(tmp < 1) return traits::eof();
  		setg(this->eback(),this->egptr(),this->egptr() + tmp);
  		
		return traits::to_int_type(*this->gptr());
 	}
};

template<class Writer , class charT, typename traits = char_traits<charT> >
class ostreambufadaptor: public basic_streambuf<charT,traits>
{
//friend  streamadaptor<class Stream, Writer> MakeStreamAdaptor<>(Stream& s, Writer& w);
//friend  class streamadaptor;
	Writer& _w;
 	charT *_out;
 	streamsize _out_len;
public: //TODO these two func should not be public 
 	ostreambufadaptor(const ostreambufadaptor&);
 	ostreambufadaptor& operator=(const ostreambufadaptor&);
public:
    typedef charT   char_type;
    typedef typename traits::int_type	    int_type;
    typedef typename traits::pos_type	    pos_type;
    typedef typename traits::off_type	    off_type;
    typedef traits			    traits_type;
 
	ostreambufadaptor(Writer& w,ios_base::openmode mode = ios_base::out | ios_base::binary): _w(w),_out(0),_out_len(0) {
  		this->setp(_out,_out + _out_len);
	}

 	~ostreambufadaptor() {
  		if(_out) free_out_buf();
 	}

private:
 	void init_out_buf() {
  		_out_len = 512;
  		_out = new charT[_out_len];
  		this->setp(_out,_out + _out_len);
 	}

 	void free_out_buf() {
  		delete [] _out;
  		_out = 0;
  		_out_len = 0;
  		this->setp(0,0);
 	}

protected:
	virtual int_type overflow(int_type c) {
  		sync();
  		if(traits::eq_int_type(c,traits::eof()) ) {
   			return traits::not_eof(c);
		} else {
   			if(this->pptr() == 0) init_out_buf();
   			return sputc(c);
  		}
 	}
 
	virtual int sync() {
  		if(this->pptr() > this->pbase()) {
   			int tmp = this->pptr() - this->pbase();
   			int tmp1 = _w.write(this->pbase(),tmp);
   			if(tmp1 < 1) {
    			return -1;
			}
   			if(tmp1 < tmp) {
    			memcpy(this->pbase(),this->pbase() + tmp1, tmp - tmp1);
    			this->setp(this->pbase(),this->epptr());
    			this->pbump(tmp1);
   			} else {
    			this->setp(this->pbase(),this->epptr());  
			}
  		}
  		return 0;
 	}
};


class streamadaptor_base{};

typedef const streamadaptor_base& IStreamAdaptor;
typedef const streamadaptor_base& OStreamAdaptor;


template<class stream,class Reader>
class istreamadaptor: public streamadaptor_base {
public:
 	typedef typename stream::char_type char_type;
 	typedef typename stream::traits_type traits_type;
private:
 	basic_ios<char_type,traits_type>& _stream ;
 	istreambufadaptor<Reader,char_type,traits_type> _buff; 
 	basic_streambuf<char_type,traits_type>* _oldbuf;
public:
 	istreamadaptor(stream& s,Reader& r):_stream(s),_buff(r) {
  		_oldbuf = _stream.rdbuf(&_buff);
 	}
 	
	~istreamadaptor() {
  		_stream.rdbuf(_oldbuf);
 	}
 
	operator stream&(){ return _stream; }
 
	stream& get() { return _stream; }
};

template<class Stream, class Reader>
istreamadaptor<Stream,Reader> MakeIStreamAdaptor(Stream& s,Reader& r) {
 	return istreamadaptor<Stream, Reader>(s,r);
};


template<class stream,class Writer>
class ostreamadaptor: public streamadaptor_base {
public:
 	typedef typename stream::char_type char_type;
 	typedef typename stream::traits_type traits_type;
private:
 	basic_ios<char_type,traits_type>& _stream ;
 	ostreambufadaptor<Writer,char_type,traits_type> _buff; 
 	basic_streambuf<char_type,traits_type>* _oldbuf;
public:
 	ostreamadaptor(stream& s,Writer& w):_stream(s),_buff(w) {
  		_oldbuf = _stream.rdbuf(&_buff);
 	}
 	
	~ostreamadaptor() {
  		_stream.rdbuf(_oldbuf);
 	}
 
	operator stream&(){ return _stream; }
 
	stream& get() { return _stream; }
};

template<class Stream, class Writer>
ostreamadaptor<Stream, Writer> MakeOStreamAdaptor(Stream& s, Writer& w) {
 	return ostreamadaptor<Stream, Writer>(s,w);
};


#define CONCATENATE_DIRECT(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#define ISTREAM_ADAPTOR IStreamAdaptor ANONYMOUS_VARIABLE(istreamAdaptor) = MakeIStreamAdaptor
#define OSTREAM_ADAPTOR OStreamAdaptor ANONYMOUS_VARIABLE(ostreamAdaptor) = MakeOStreamAdaptor

};
